package com.cloudkinto.service.platform.impl;

import cn.afterturn.easypoi.csv.entity.CsvExportParams;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloudkinto.common.RabbitQueueConstant;
import com.cloudkinto.common.RedisKeyConstants;
import com.cloudkinto.common.common.DateUtils;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.CsvUtils;
import com.cloudkinto.common.utils.TimeUtils;
import com.cloudkinto.common.utils.XmlUtils;
import com.cloudkinto.common.utils.YahooUtils;
import com.cloudkinto.common.utils.ali.AliOss;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.dao.*;
import com.cloudkinto.dto.platform.rakuten.OrderRakutenStatusDto;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.common.CountryEnum;
import com.cloudkinto.enumType.operate.OperateTypeEnum;
import com.cloudkinto.service.consumer.vo.StockToPlatformConsumerReq;
import com.cloudkinto.service.consumer.vo.order.PlatformOrderCancelReq;
import com.cloudkinto.service.consumer.vo.order.UpdateInventoryLessReq;
import com.cloudkinto.service.consumer.vo.orderSyncRecord.PlatformOrderSyncRecordReq;
import com.cloudkinto.service.consumer.vo.rakuten.RakutenOrderUpdateConReq;
import com.cloudkinto.service.consumer.vo.yahoo.*;
import com.cloudkinto.service.mail.vo.SendMailConsumerReq;
import com.cloudkinto.service.order.PlatformOrderCopyService;
import com.cloudkinto.service.platform.PlatformOrderService;
import com.cloudkinto.service.platform.PlatformOrderYahooSyncService;
import com.cloudkinto.service.platform.PlatformProductService;
import com.cloudkinto.service.platform.PlatformStoreService;
import com.cloudkinto.service.platform.sync.vo.SyncRecordItemReq;
import com.cloudkinto.service.platform.vo.AsyncFinishConsumerReq;
import com.cloudkinto.service.platform.vo.AsyncUpdateConsumerReq;
import com.cloudkinto.service.platform.vo.StockToPlatformRecordVo;
import com.cloudkinto.service.platform.vo.rakuten.PlatformProductSyncReq;
import com.cloudkinto.service.platform.vo.store.StoreYahooAddReq;
import com.cloudkinto.service.platform.vo.yahoo.*;
import com.cloudkinto.service.platform.vo.yahoo.stock.YahooSkuSetStockReq;
import com.cloudkinto.vo.stock.PlatformSkuStockDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jdom2.JDOMException;
import org.json.XML;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * service实现类
 * </p>
 *
 * @author ding
 * @since 2021-09-23
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)//事务回滚
@RequiredArgsConstructor
public class PlatformOrderYahooSyncServiceImpl extends ServiceImpl<PlatformOrderDao, PlatformOrderDo> implements PlatformOrderYahooSyncService {
    private final PlatformStoreDao platformStoreDao;
    private final PlatformStoreService platformStoreService;
    private final PlatformOrderDao platformOrderDao;
    private final RabbitTemplate rabbitTemplate;
    private final PlatformOrderProductDao platformOrderProductDao;
    private final PlatformOrderService platformOrderService;
    private final PlatformProductService platformProductService;
    protected final RedisTemplate redisTemplate;
    private final AsyncJobDao asyncJobDao;
    private final ProductStockDao productStockDao;
    @Value("${gbc.auth.redirectURL}")
    private String redirectURL;
    @Value("${gbc.customer}")
    private String customer;
    @Value("${gbc.exportPath.omsFilePath}")
    private String omsFilePath;
    @Value("${gbc.uploadPath.omsFileUrl}")
    private String omsFileUrl;
    @Value("${gbc.uploadPath.fileUrl}")
    private String fileUrl;
    @Value("${gbc.uploadPath.filePath}")
    private String filePath;
    private final PlatformOrderSyncRecordDao platformOrderSyncRecordDao;

    private String AUTHORIZESTATEKEY = "yahoo_authorize_state_";

    private final String yahooScope = "openid%20profile%20email%20address";
    private final PlatformOrderCopyService platformOrderCopyService;
    private final ThreadPoolTaskExecutor taskExecutor;


    /**
     * Yahoo 用户授权第一步， 获取授权url，前端直接跳转到该页面 ,
     */
    @Override
    public String getYahooAuthorizeUrl(StoreYahooAddReq req, Long userId, Long companyId) {
        boolean check = platformStoreService.check(req.getId(), req.getStoreCode(), companyId);
        if (!check) {
            throw new BizException(SysConstant.Shop_Code_Exist);
        }
        if (req.getId() != null) {
            PlatformStoreDo storeDo = platformStoreService.getById(req.getId());
            if (storeDo == null || !Objects.equals(storeDo.getCompanyId(), companyId)) {
                throw new BizException(SysConstant.Shop_NotExist);
            }
        }
        String state = UUID.randomUUID().toString();
        //放入缓存
        redisTemplate.opsForValue().set(AUTHORIZESTATEKEY + userId, state, 360, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set(state, JSON.toJSONString(req), 360, TimeUnit.SECONDS);
        String authorizeUrl = "https://auth.login.yahoo.co.jp/yconnect/v2/authorization?client_id=" + YahooUtils.clientId + "&redirect_uri=" + redirectURL + "&response_type=code&scope=" + yahooScope + "&state=" + state;//生产
        return authorizeUrl;
        //授权页面，如果用户同意授权， 会返回一个code和state(我们传过去的)
        //错误会返回 error 和 error_description 和 error_code
    }

    /**
     * Yahoo 用户授权第二部 根据上一步返回的 code refreshToken(刷新令牌,有效期为 4 周)
     */
    @Override
    public String getYahooRefreshTokenByCode(String code, String state, Long userId, Long companyId) {
        //验证code 和state值
        Object value = redisTemplate.opsForValue().get(state);
        if (value == null) {
            throw new BizException(SysConstant.Store_Auth_notExistMsg);
        }
        StoreYahooAddReq storeYahooAddReq = JSON.parseObject(value.toString(), StoreYahooAddReq.class);
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("grant_type", "authorization_code");
        paramMap.put("code", code);
        paramMap.put("redirect_uri", redirectURL);
        String body = null;//{"error":"unsupported_grant_type","error_description":"grant type in request is not supported by the authorization server"}
        try {
            body = YahooUtils.yahooDoPostAuth("https://auth.login.yahoo.co.jp/yconnect/v2/token", storeYahooAddReq.getSellerId(), storeYahooAddReq.getAppSecret(), storeYahooAddReq.getSignatureVersion(), paramMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (StringUtils.isBlank(body)) {
            throw new BizException("连接超时");
        }
        JSONObject jsonObject = JSONObject.parseObject(body);
        if (StringUtils.isNotBlank(jsonObject.getString("error_description"))) {
            throw new BizException(jsonObject.getString("error_description"));
        }

        PlatformStoreDo storeDo;
        if (storeYahooAddReq.getId() == null) {
            storeDo = platformStoreService.yahooStoreAdd(storeYahooAddReq, userId);
        } else {
            storeDo = platformStoreService.getById(storeYahooAddReq.getId());
        }
        if (storeDo == null || !Objects.equals(storeDo.getCompanyId(), companyId)) {
            throw new BizException(SysConstant.Shop_NotExist);
        }
        String access_token = jsonObject.getString("access_token");
        String token_type = jsonObject.getString("token_type");
        Long expires_in = jsonObject.getLong("expires_in");
        String refresh_token = jsonObject.getString("refresh_token");//有效期为 4 周。最大长度为 512 字节长的字符串
        String id_token = jsonObject.getString("id_token");
        if (StringUtils.isBlank(access_token) || expires_in == null || StringUtils.isBlank(refresh_token) || StringUtils.isBlank(id_token)) {
            throw new BizException("授权失败");//有日文翻译
        }
        //todo 验证 id_token
        //refresh_token 是长期令牌(4周) 后期保存到 t_shop表中 todo
        storeDo.setServiceToken(refresh_token);
        storeDo.setSellerId(storeYahooAddReq.getSellerId());
        storeDo.setAppSecret(storeYahooAddReq.getAppSecret());
        storeDo.setSignatureVersion(storeYahooAddReq.getSignatureVersion());
        storeDo.setAuthSyncStatus(1);
        storeDo.setAuthSyncTime(new Date());
        storeDo.setAuthExpireTime(DateUtils.passingDate(new Date(), 20));
        platformStoreDao.updateById(storeDo);

        redisTemplate.opsForValue().set(RedisKeyConstants.YAHOOACCESSTOKEN_STORE + storeDo.getId(), access_token, expires_in - 30, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set(RedisKeyConstants.YAHOOREFRESHTOKEN_STORE + storeDo.getId(), refresh_token, 3600 * 24 * 28L - 30, TimeUnit.SECONDS);

        Map<String, Object> consumerParam = new HashMap<>();
        consumerParam.put("shopId", storeDo);
        consumerParam.put("shopName", storeDo.getStoreName());
        //获取Yahoo sellerId
        redisTemplate.convertAndSend(RabbitQueueConstant.PlatformStore_Yahoo_UserInfo, JSON.toJSONString(consumerParam));
        return access_token;
    }


    @Override
    public Map<String, Object> getYahooUserInfoApi(Long storeId) {
        PlatformStoreDo storeDo = platformStoreDao.selectById(storeId);

        String body = null;
        Map<String, String> param = new HashMap<>();
//        param.put("callback", "success");//如果指定了 则以 Content-Type: text/javascript 形式返回响应。
        try {
            body = YahooUtils.yahooDoGet("https://userinfo.yahooapis.jp/yconnect/v2/attribute", getYahooUserAccessToken(storeDo), param);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Map<String, Object> result = new HashMap<>();
        if (org.apache.commons.lang3.StringUtils.isBlank(body)) {
            result.put("code", "FAIL");
            result.put("msg", "Yahoo获取用户信息api 连接超时");
            return result;
        }
        JSONObject jsonObject = JSONObject.parseObject(body);
        if (org.apache.commons.lang3.StringUtils.isNotBlank(jsonObject.getString("error_description"))) {
            result.put("code", "FAIL");
            result.put("msg", jsonObject.getString("error_description"));
            return result;
        } else {
            //获取成功 保存
            YahooUserInfo userInfo = JSONObject.parseObject(body, YahooUserInfo.class);
            storeDo.setSellerId(userInfo.getSub());
            platformStoreDao.updateById(storeDo);
            result.put("code", "SUCCESS");
        }
        return result;
    }


    @Override
    public void platOrderYahooSync() {
        List<PlatformStoreDo> storeDoList = platformStoreDao.selectList(new LambdaQueryWrapper<PlatformStoreDo>().eq(PlatformStoreDo::getPlatformCode, "yahoo")
                .eq(PlatformStoreDo::getAuthSyncStatus, 1).isNotNull(PlatformStoreDo::getAppSecret).isNotNull(PlatformStoreDo::getSellerId));
        Map<Long, Long> storeRecordIdMap = new HashMap<>();
        for (PlatformStoreDo storeDo : storeDoList) {
            PlatformOrderSyncRecordDo recordDo = new PlatformOrderSyncRecordDo(storeDo.getPlatformCode(), storeDo.getId(), 1, 0, 0, new Date(), null, storeDo.getCompanyId());
            platformOrderSyncRecordDao.insert(recordDo);
            storeRecordIdMap.put(storeDo.getId(), recordDo.getId());
        }
        for (PlatformStoreDo platformStoreDo : storeDoList) {
            Long syncRecordId = storeRecordIdMap.get(platformStoreDo.getId());
            if (syncRecordId == null) {
                continue;
            }
            YahooConsumerReq consumerReq = new YahooConsumerReq(platformStoreDo.getId(), null, null, null, true, syncRecordId);
            rabbitTemplate.convertAndSend(RabbitQueueConstant.Labella_PlatformStore_Yahoo_Sync, JSON.toJSONString(consumerReq));
        }
    }


    @Override
    public Object testGetOrderByNumber(String orderNumber, Long storeId) {
        Map<String, Object> result = new HashMap<>();
        PlatformStoreDo storeDo = platformStoreDao.selectById(storeId);
        if (storeDo == null) {
            return null;
        }
        YahooConsumerReq consumerReq = new YahooConsumerReq(storeDo.getId(), null, null, orderNumber, false);
        String body = this.getOrder(2000, 1, consumerReq, storeDo, "manual");
        log.info("yahoo 订单返回值：" + body);
        if (StringUtils.isBlank(body)) {
            return "连接超时";
        }
        OrderYahooEntity detailEntity = this.getYahooOrderDetailInfo(storeDo, orderNumber, "manual");
        result.put("detail", detailEntity);
        JSONObject object = null;
        List<OrderYahooEntity> list = null;
        try {
            object = XmlUtils.xml2Json(body);
        } catch (JDOMException | IOException e) {
            e.printStackTrace();
        }
        result.put("search", object);
        return result;
    }


    @Override
    public String syncOrderManual(String orderNumber, Date beginTime, Date endTime, Long storeId) {
        YahooConsumerReq consumerReq = new YahooConsumerReq(storeId,beginTime, endTime, orderNumber, false);
        String string = this.updateYahooOrders(consumerReq);
        return string;
    }


    @Override
    public String updateYahooOrders(YahooConsumerReq req) {
        PlatformStoreDo storeDo = platformStoreDao.selectById(req.getStoreId());
        if (storeDo == null) {
            return SysConstant.Shop_NotExist;
        }
        boolean flag = req.isAuto();
        //自动
        if (!flag && req.getOrderTimeFrom() != null) {
            long l = DateUtil.betweenDay(req.getOrderTimeFrom(), req.getOrderTimeTo(), true);
            if (l > 7) {
                return "店铺:" + storeDo.getStoreName() + "每次最多同步7天数据";
            }
        }
        if (flag) { //自动
            Double time = redisTemplate.opsForZSet().score(RedisKeyConstants.orderSyncTimeSet_Yahoo + customer, storeDo.getId());
            long currency = System.currentTimeMillis();
            if (!(time == null || (currency - time) >= 60 * 5 * 1000)) {
//                return "yahoo 同步频率过快(5分钟同步一次)";
            }
            //无上次同步时间 或者 上次同步时间在10分钟之前
            redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo + customer, storeDo.getId(), currency - 5 * 1000);
            if (time == null) {
                req.setOrderTimeFrom(DateUtils.passingDateHour(new Date(), -16));
            } else {
                req.setOrderTimeFrom(new Date(time.longValue() - 60 * 1000 * 21));
            }
            req.setOrderTimeTo(new Date(currency));
            long l = DateUtil.betweenDay(req.getOrderTimeFrom(), req.getOrderTimeTo(), true);
            if (l > 2) {
                req.setOrderTimeFrom(DateUtils.passingDate(new Date(), -2));
            }
        }

        int Result = 2000;   ///Req/Search/Result
        int Start = 1;       ///Req/Search/Start
        String Sort = "+order_time";///Req/Search/Sort
        //还有一个/Req/Search/Condition/

        //* 1需要 OrderID 或 OrderTime YYYYMMDDHH24MISS。
        //* 2如果未指定 OrderID，则需要 OrderTime、OrderTimeFrom、OrderTimeTo、PublicationTimeFrom 和 PublicationTimeTo 之一。
        //* 3指定以逗号分隔的多个匹配项。

        int total = 0;
        //告诉他正在处理
        rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformOrder_SyncRecord_Update, JSON.toJSONString(new PlatformOrderSyncRecordReq(req.getSyncId(), 1, new Date(), req.getOrderTimeFrom(), req.getOrderTimeTo())));
        List<OrderYahooEntity> entities = new ArrayList<>();
        do {
            //拼接参数 并且 转成日本时间
            String body = this.getOrder(Result, Start, req, storeDo, "auto");
            Start += Result;
            if (StringUtils.isBlank(body)) {
                if (flag) {
                    redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                }
                return "连接超时";
            }

            //<?xml version='1.0' encoding='UTF-8' ?>
            //<Error>
            //<Message>AccessToken has been expired. This API session is shorter than another API.</Message>
            //<Code>px-04102</Code>
            //</Error>
            JSONObject object = null;
            List<OrderYahooEntity> list = null;
            try {
                object = XmlUtils.xml2Json(body);
            } catch (JDOMException | IOException e) {
                e.printStackTrace();
            }
            if (object == null) {
                if (flag) {
                    redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                }
                return "Yahoo 获取订单列表 xml格式解析空指针";
            } else if (StringUtils.isNotBlank(object.getString("Error"))) {
                String errorCode = object.getJSONObject("Error").getString("Code");
                String message = object.getJSONObject("Error").getString("Message");
                if ("px-04102".equals(errorCode) || "Bad Request: Authentication parameters in your request incompleted.".equals(message)) {
                    platformStoreService.updateStoreAuthStatus(storeDo.getId());
                }
                if (flag) {
                    redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                }
                return errorCode +":" +message;
            } else {
                total = object.getJSONObject("Result").getJSONArray("Search").getJSONObject(0).getInteger("TotalCount");
                if (total == 0) {
                    //<Result><Status>OK</Status><Search><TotalCount>0</TotalCount></Search></Result>
                    list = new ArrayList<>();
                } else {
                    list = JSONArray.parseArray(object.getJSONObject("Result").getJSONArray("Search").getJSONObject(0).getString("OrderInfo"), OrderYahooEntity.class);
                }
            }
            if (list == null) {
                if (flag) {
                    redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                }
                return "Yahoo 获取订单列表 xml格式解析 数组null";
            }
            entities.addAll(list);
        } while (total >= Result);


        //根据同步下来的订单号 获取本地订单情况
        Map<String, OrderRakutenStatusDto> orderMap = new HashMap<>();
        List<String> localFinishOrderIds = new ArrayList<>();
        List<String> orderIds = entities.stream().map(OrderYahooEntity::getOrderId).collect(Collectors.toList());
        List<List<String>> partition = ListUtil.partition(orderIds, 5000);
        for (List<String> list : partition) {
            List<OrderRakutenStatusDto> rakutenOrder = platformOrderDao.getRakutenOrder(list, req.getStoreId());
            localFinishOrderIds.addAll(rakutenOrder.stream()
                    .filter(i -> StaticDict.Platform_Order_Status.Three.getValue().equals(i.getStatus()) || StaticDict.Platform_Order_Status.Four.getValue().equals(i.getStatus()))
                    .map(OrderRakutenStatusDto::getPlatformOrderNo).collect(Collectors.toList()));
            orderMap.putAll(rakutenOrder.stream().collect(Collectors.toMap(OrderRakutenStatusDto::getPlatformOrderNo,
                    i -> i)));
        }

        //组装
        //要插入的
        List<PlatformOrderDo> platInertList = new ArrayList<>();
        //要更新 子表的的
        List<PlatformOrderDo> platUpdateList = new ArrayList<>();
        //需要修改订单状态为待发货的订单
        List<YahooMarkStatusReq> markStatusReqList = new ArrayList<>();
        //需要修改订单状态为待付款的订单
        List<YahooMarkStatusReq> markStatusNoPayReqList = new ArrayList<>();
        //子表插入的
        List<PlatformOrderProductDo> platInsertItems = new ArrayList<>();
        List<PlatformOrderProductDo> platUpdateItems = new ArrayList<>();

        int ii = 1;
        //然后一条条 获取详情方法
        for (OrderYahooEntity entity : entities) {
//            if ("0".equals(entity.getPayStatus()) && "0".equals(entity.getShipStatus())) {
//                //未付款的 不可发货状态的订单，不保存
//                continue;
//            }
//            if (localFinishOrderIds.contains(entity.getOrderId())) {  //本地 已发货的订单剔除 -> 本地存在的订单剔除 不做更新
//                continue;
//            }
            OrderRakutenStatusDto oldOrder = orderMap.get(entity.getOrderId());
            if (oldOrder != null &&
                    (StaticDict.Platform_Order_Status.Three.getValue().equals(oldOrder.getStatus()) || StaticDict.Platform_Order_Status.Four.getValue().equals(oldOrder.getStatus()))) {
                //本地订单是已完成状态
                continue;
            }
            PlatformOrderDo orderDo = new PlatformOrderDo();
            orderDo.setReserveFlag(0);
            orderDo.setOrderNo(entity.getPayStatus() + "_" + entity.getShipStatus());
            orderDo.setCompanyId(storeDo.getCompanyId());
            orderDo.setTenantId(storeDo.getTenantId());
            orderDo.setPlatformCode(storeDo.getPlatformCode());
            orderDo.setStoreId(storeDo.getId());
            orderDo.setSite(storeDo.getSite());
            orderDo.setOrderTime(DateUtils.stringFormatToDate(entity.getOrderTime(), "yyyy-MM-dd'T'HH:mm:ss"));
            if (Objects.equals(entity.getPayStatus(), "1")) {
                orderDo.setPayTime(orderDo.getOrderTime());
            }
//            if (StringUtils.isNotBlank(entity.getPayDate())) {
//                orderDo.setPayTime(DateUtils.stringFormatToDate(entity.getPayDate()));
//            }
            orderDo.setLastUpdateDate(DateUtils.stringFormatToDate(entity.getLastUpdateTime(), "yyyy-MM-dd'T'HH:mm:ss"));
            orderDo.setPlatformOrderNo(entity.getOrderId());
            orderDo.setSettlementModel(entity.getPayMethodName());
            if ("キャンセル".equals(entity.getOrderStatus()) || "4".equals(entity.getOrderStatus())) {
                orderDo.setStatus(StaticDict.Platform_Order_Status.Four.getValue());
                orderDo.setCancelWay(0);
            } else if ("完了".equals(entity.getOrderStatus()) || "5".equals(entity.getOrderStatus())) {
                orderDo.setStatus(StaticDict.Platform_Order_Status.Three.getValue());
                if (StringUtils.isNotBlank(entity.getShipDate())) {
                    try {
                        orderDo.setShipTime(DateUtils.stringFormatToDate(entity.getShipDate(), "yyyy-MM-dd"));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
//                orderDo.setMarkShipmentStatus(4);
            } else {
                if (oldOrder == null) {//只有插入的时候才设置，不然每次更新都会更新成待处理状态
                    orderDo.setStatus(StaticDict.Platform_Order_Status.Zero.getValue());
                }
            }
            //order的status 等于0 (代表第一次同步) 或者 (order的status 等于0 (代表本地同步过) 并且 oldOrder的status 等于 0 或者 5)
            boolean status0Flag = StaticDict.Platform_Order_Status.Zero.getValue().equals(orderDo.getStatus()) ||
                    (orderDo.getStatus() == null && oldOrder != null && (StaticDict.Platform_Order_Status.Zero.getValue().equals(oldOrder.getStatus())
                            || StaticDict.Platform_Order_Status.Five.getValue().equals(oldOrder.getStatus())
                    ));
            if (StringUtils.isNotBlank(entity.getShipRequestDate()) && status0Flag && orderDo.getPayTime() != null) {
                try {
                    Date deliverDate = DateUtil.parse(entity.getShipRequestDate(), "yyyy-MM-dd");
                    if (DateUtil.offsetDay(deliverDate, -7).isAfter(new Date())) {
                        orderDo.setStatus(StaticDict.Platform_Order_Status.Five.getValue());
                        orderDo.setExceptionCause("指定配达日期:" + entity.getShipRequestDate() + " 超过7天");
                    } else {
                        orderDo.setStatus(StaticDict.Platform_Order_Status.Zero.getValue());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (oldOrder != null && oldOrder.getPayTime() != null && (orderDo.getStatus() == null || orderDo.getStatus() == oldOrder.getStatus())) {
                continue;
            }
            OrderYahooEntity detailEntity = this.getYahooOrderDetailInfo(storeDo, entity.getOrderId(), "auto");
            if (detailEntity == null) {
                if (flag) {
                    redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                }
                break;
            }
            //收货人信息
            if (detailEntity.getShip() != null && !detailEntity.getShip().isEmpty()) {
                OrderYahooShipEntity shippingAddress = detailEntity.getShip().get(0);
                orderDo.setReceiver(shippingAddress.getShipName());//ship 里面收件人就是null
                if (StringUtils.isBlank(orderDo.getReceiver())) {
                    orderDo.setReceiver(entity.getShipName());
                }
                orderDo.setTelephone(shippingAddress.getShipPhoneNumber());
                orderDo.setCountryRegionCode(CountryEnum.JP.toString());//todo yahoo的国家
//                orderDo.setProvinceCode(shippingAddress.getProvince_code());
//                orderDo.setProvinceName(shippingAddress.getProvince());
                orderDo.setCityCode(shippingAddress.getShipCity());
                orderDo.setDistrict(shippingAddress.getShipPrefecture());
                orderDo.setPostcode(shippingAddress.getShipZipCode());
                orderDo.setHouseNum("");
                orderDo.setAddressOne(shippingAddress.getShipAddress1());
                orderDo.setAddressTwo(shippingAddress.getShipAddress2());
            }
            orderDo.setBuyerName(entity.getBuyerName());
            if (detailEntity.getPay() != null && !detailEntity.getPay().isEmpty()) {
                orderDo.setBuyerEmail(detailEntity.getPay().get(0).getBillMailAddress());
            }
            //买家信息
            orderDo.setDefaultDeliveryCompanyCode(entity.getShipMethodName());
            orderDo.setTaxRate(BigDecimal.valueOf(0.1));
            if (entity.getTotalPrice() != null) {
//                orderDo.setTotalAmount(BigDecimal.valueOf(entity.getTotalPrice()));
                orderDo.setReqPrice(BigDecimal.valueOf(entity.getTotalPrice()));
                orderDo.setCurrencyCode(CountryEnum.JP.getCurrency());
                orderDo.setReqPriceTax(BigDecimal.valueOf(entity.getTotalPrice() / 1.1 * 0.1));
            }
            //积分
            if (entity.getUsePoint() != null) {
                orderDo.setPoint(BigDecimal.valueOf(entity.getUsePoint()));
            }
            //商品卷金额(GiftCardDiscount) 折扣(Discount)
            if (entity.getGiftWrapCharge() != null || entity.getDiscount() != null) {
                BigDecimal coupon = new BigDecimal(0);
                if (entity.getGiftWrapCharge() != null) {
                    coupon = coupon.add(BigDecimal.valueOf(entity.getGiftWrapCharge()));
                }
                if (entity.getDiscount() != null) {
                    coupon = coupon.add(BigDecimal.valueOf(entity.getDiscount()));
                }
                if (entity.getTotalMallCouponDiscount() != null) {
                    coupon = coupon.add(BigDecimal.valueOf(entity.getTotalMallCouponDiscount()));
                }
                if (entity.getGiftCardDiscount() != null) {
                    coupon = coupon.add(BigDecimal.valueOf(entity.getGiftCardDiscount()));
                }
                orderDo.setCoupon(coupon);
            }

            //运费(ShipCharge) 包装费(GiftWrapCharge) 设定手续费(PayCharge)
            BigDecimal postagePrice = new BigDecimal(0);
            if (entity.getShipCharge() != null) {
                postagePrice = postagePrice.add(BigDecimal.valueOf(entity.getShipCharge()));
            }
            if (entity.getPayCharge() != null) {
                postagePrice = postagePrice.add(BigDecimal.valueOf(entity.getPayCharge()));
            }
            if (entity.getGiftWrapCharge() != null) {
                postagePrice = postagePrice.add(BigDecimal.valueOf(entity.getGiftWrapCharge()));
            }
            if (entity.getAdjustments() != null) {
                postagePrice = postagePrice.add(BigDecimal.valueOf(entity.getAdjustments()));
            }
            orderDo.setPostagePrice(postagePrice);
            if (StaticDict.Company_Type.Labella.getValue().equals(customer)) {
                //yahoo 店铺 KOISAMA 默认次日达
                if (StringUtils.isNotBlank(entity.getShipRequestDate()) || storeDo.getId() == 5L) {
                    orderDo.setReserveFlag(2);
                    orderDo.setDeliveryDate(entity.getShipRequestDate());
                    orderDo.setDeliveryTimeName(entity.getShipRequestTime());
                }
            } else {
                if (StringUtils.isNotBlank(entity.getShipRequestDate())) {
                    orderDo.setReserveFlag(0);
                    orderDo.setDeliveryDate(entity.getShipRequestDate());
                    orderDo.setDeliveryTimeName(entity.getShipRequestTime());
                }
            }
            if ("1".equals(entity.getOrderStatus())) {
                orderDo.setReserveFlag(1);
                if (StringUtils.isNotBlank(entity.getReleaseDate())) {
                    orderDo.setReserveEndTime(DateUtils.stringFormatToDate(entity.getReleaseDate()));
                }
                orderDo.setReserveBeginTime(orderDo.getOrderTime());
            }
            if (entity.getShipRequestTime() != null) {
                orderDo.setDeliveryTimeName(entity.getShipRequestTime().replaceAll(":00", ""));
            }
            StringBuilder builder = new StringBuilder();
            BigDecimal totalAmount = new BigDecimal(0);
            for (OrderYahooItemEntity item : detailEntity.getItem()) {
                if ((StaticDict.Company_Type.Labella.getValue().equals(customer)
                        || StaticDict.Company_Type.Lemooli.getValue().equals(customer))
                        && StringUtils.isNotBlank(item.getLeadTimeText()) && item.getLeadTimeText().contains("10時までの注文で当日発送")) {
                    orderDo.setReserveFlag(2);
                }
                PlatformOrderProductDo platItem = new PlatformOrderProductDo();
                platItem.setPlatformOrderNo(entity.getOrderId());
                platItem.setPlatformParentSku(item.getItemId());
                platItem.setPlatformSku(item.getSubCode());
                if (StringUtils.isBlank(item.getSubCode())) {
                    platItem.setPlatformSku(item.getItemId());
                }
                platItem.setPlatformProductName(item.getTitle());
                platItem.setPlatformQty(item.getQuantity());
                platItem.setSkuType(1);
                if (StringUtils.isNotBlank(item.getUnitPrice())) {
                    platItem.setItemAmount(BigDecimal.valueOf(Long.parseLong(item.getUnitPrice())));
                }
                totalAmount = totalAmount.add(platItem.getItemAmount().multiply(BigDecimal.valueOf(platItem.getPlatformQty())));
                platItem.setCurrencyCode(CountryEnum.JP.getCurrency());
                if (org.apache.commons.lang3.StringUtils.isNotBlank(builder)) {
                    builder.append(";");
                }
                builder.append(platItem.getPlatformSku()).append("*").append(platItem.getPlatformQty());
                if (oldOrder != null) {
                    platItem.setOrderId(oldOrder.getId());
                    platUpdateItems.add(platItem);
                } else {
                    platInsertItems.add(platItem);
                }
            }
            orderDo.setTotalAmount(totalAmount);
            if (builder.length() > 100) {
                builder.delete(100, builder.length());
            }
            orderDo.setPlatformSku(builder.toString());
            if (oldOrder != null) {//只更主表不更新子表
                if (Objects.equals(orderDo.getStatus(), oldOrder.getStatus()) && Objects.equals(orderDo.getPayTime(), oldOrder.getPayTime())) {
                    //状态一致 忽略
                    continue;
                }
                orderDo.setId(oldOrder.getId());
                platUpdateList.add(orderDo);
                if ("2".equals(entity.getOrderStatus()) && !entity.getIsSeen()) {
                    markStatusReqList.add(new YahooMarkStatusReq(orderDo.getId(), orderDo.getPlatformOrderNo()));
                }
            } else {
                platInertList.add(orderDo);
                if (("1".equals(entity.getOrderStatus()) || "2".equals(entity.getOrderStatus())) && !entity.getIsSeen()) {
                    markStatusReqList.add(new YahooMarkStatusReq(null, orderDo.getPlatformOrderNo()));
                }
            }
        }

        if (!platInertList.isEmpty()) {
            platformOrderService.matchSystemSku(platInsertItems, storeDo.getId(), storeDo.getCompanyId(), storeDo.getTenantId());
            Map<String, List<PlatformOrderProductDo>> skuMap = platInsertItems.stream().filter(i -> org.apache.commons.lang3.StringUtils.isNotBlank(i.getSku())).collect(Collectors.groupingBy(PlatformOrderProductDo::getPlatformOrderNo));
            List<PlatformOrderDo> insertList = platInertList.stream().filter(i -> skuMap.containsKey(i.getPlatformOrderNo())).collect(Collectors.toList());
            for (PlatformOrderDo orderDo : insertList) {
                if (skuMap.get(orderDo.getPlatformOrderNo()) == null) {
                    continue;
                }
                StringBuilder builder = new StringBuilder();
                for (PlatformOrderProductDo productDo : skuMap.get(orderDo.getPlatformOrderNo())) {
                    if (org.apache.commons.lang3.StringUtils.isNotBlank(builder.toString())) {
                        builder.append(";");
                    }
                    builder.append(productDo.getSku()).append("*").append(productDo.getQty());
                }
                if (builder.length() > 100) {
                    builder.delete(100, builder.length());
                }
                orderDo.setSku(builder.toString());
            }
            platformOrderDao.insertPlatformOrder(platInertList);

            for (PlatformOrderDo orderDo : platInertList) {
                //去赋值 orderId
                platInsertItems.stream().filter(i -> i.getPlatformOrderNo().equals(orderDo.getPlatformOrderNo())).forEach(item -> item.setOrderId(orderDo.getId()));
                markStatusReqList.stream().filter(i -> i.getId() == null && Objects.equals(i.getOrderId(), orderDo.getPlatformOrderNo())).forEach(i -> i.setId(orderDo.getId()));
            }
        }
        //插入子表
        if (!platInsertItems.isEmpty()) {
            platformOrderProductDao.insertPlatformOrderItems(platInsertItems);
        }

        if (StringUtils.isBlank(req.getOrderId()) && flag) {
//        //追加本地未完成的订单 300
            //每四次自动同步 同步一次
            Object o = redisTemplate.opsForValue().
                    get(RedisKeyConstants.orderSyncTimeCountSet_Yahoo + storeDo.getId() + "_" + customer + "_" + DateUtils.dateFormatToString(new Date()));
            LocalTime now = LocalTime.now();
            int hour = now.getHour();
            if (o == null || Integer.parseInt(o.toString()) % 4 == 0 || hour == 11) {
                int timeCount = o == null ? 0 : Integer.parseInt(o.toString()) + 1;
                redisTemplate.opsForValue().set(RedisKeyConstants.orderSyncTimeCountSet_Yahoo + storeDo.getId() + "_" + customer + "_" + DateUtils.dateFormatToString(new Date()), timeCount, 6 * 60 * 60, TimeUnit.SECONDS);
                LambdaQueryWrapper<PlatformOrderDo> queryWrapper = new LambdaQueryWrapper<PlatformOrderDo>().in(PlatformOrderDo::getStatus, 0, 5);
                queryWrapper.eq(PlatformOrderDo::getStoreId, req.getStoreId()).eq(PlatformOrderDo::getPlatformCode, "yahoo");
                queryWrapper.ge(PlatformOrderDo::getOrderTime, DateUtils.dateFormatToString(DateUtils.passingDate(new Date(), -30)));
//            queryWrapper.le(PlatformOrderDo::getOrderTime, DateUtils.dateFormatToString(DateUtils.passingDate(new Date(), -1)));
                List<String> localOrderIds = platformOrderDao.selectList(queryWrapper).stream().map(PlatformOrderDo::getPlatformOrderNo).filter(platformOrderNo -> !orderIds.contains(platformOrderNo)).collect(Collectors.toList());
                if (!localOrderIds.isEmpty()) {
                    List<List<String>> orderListList = ListUtil.partition(localOrderIds, 50);
                    for (List<String> list : orderListList) {
                        YahooConsumerReq consumerReq = new YahooConsumerReq(storeDo.getId(), null, null, StringUtils.join(list, ","), false);
                        rabbitTemplate.convertAndSend(RabbitQueueConstant.Labella_PlatformStore_Yahoo_Sync, JSON.toJSONString(consumerReq));
                    }
                }
            }
        }
        //分别去插入更新
        if (!platUpdateList.isEmpty()) {
            //再交给rabbitMq 去更新子表
            for (PlatformOrderDo orderDo : platUpdateList) {
                List<PlatformOrderDo> array = new ArrayList<>(Collections.singletonList(orderDo));
                List<PlatformOrderProductDo> collect = platUpdateItems.stream().filter(i -> Objects.equals(orderDo.getId(), i.getOrderId())).collect(Collectors.toList());
                rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformStore_Yahoo_Update, JSON.toJSONString(new RakutenOrderUpdateConReq(array, collect, storeDo.getId(), storeDo.getCompanyId(), storeDo.getTenantId())));
            }
        }
        log.info("yahoo更新状态为2--开始");
        List<YahooMarkStatusReq> statusReqs = markStatusReqList.stream().filter(item -> item.getId() != null).collect(Collectors.toList());
        log.info(JSONObject.toJSONString(statusReqs));
        if (!statusReqs.isEmpty()) {
            rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformStore_Yahoo_MarkStatus, JSON.toJSONString(new YahooMarkStatusConsumer(statusReqs, 2, storeDo, null)));
        }
        List<SyncRecordItemReq> items = platInertList.stream().map(i -> new SyncRecordItemReq(i.getId(), i.getPlatformOrderNo(), storeDo.getId(), storeDo.getCompanyId())).collect(Collectors.toList());
        rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformOrder_SyncRecord_Update, JSON.toJSONString(new PlatformOrderSyncRecordReq(req.getSyncId(), 2, new Date(), null, null, platInertList.size(), items)));
        // if (!markStatusNoPayReqList.isEmpty()) {
        //     rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformStore_Yahoo_MarkStatus, JSON.toJSONString(new YahooMarkStatusConsumer(markStatusNoPayReqList, 2, storeDo, null)));
        // }
        if (flag) {
            rabbitTemplate.convertAndSend(RabbitQueueConstant.StockToPlatform_CreateStockToPlatformAfterSync, storeDo.getId()+"");
        }
        return "";
    }

    public String getOrder(int Result, int Start, YahooConsumerReq req, PlatformStoreDo storeDo, String type) {
        String field = "OrderId,Version,OriginalOrderId,ParentOrderId,DeviceType,IsSeen,IsSplit,IsRoyalty," +
                "RoyaltyFixTime,IsSeller,IsRatingB2s,OrderTime,ReleaseDate,LastUpdateTime,Suspect,OrderStatus," +
                "StoreStatus,PayStatus,SettleStatus,PayType,PayMethod,PayMethodName,PayDate,SettleId,BillMailAddress,SocialGiftType,BillFirstName," +
                "BillLastName,BillPrefecture,ShipStatus,ShipMethod,ShipMethodName,ShipRequestDate,ShipRequestTime,ShipNotes," +
                "ShipCompanyCode,ReceiveShopCode,ShipInvoiceNumber1,ShipInvoiceNumber2,ShipInvoiceNumberEmptyReason,ShipUrl," +
                "SettlePayAmount,CombinedPayMethodAmount,UsePaypayPoint,"+
                "ArriveType,ShipDate,ShipFirstName,ShipLastName,ShipPrefecture,PayCharge,PayCharge,GiftWrapCharge," +
                "Discount,UsePoint,TotalPrice,RefundTotalPrice,sellerId,PayNo,ArrivalDate,printBillFlag,Adjustments,GiftCardDiscount,TotalMallCouponDiscount,GiftWrapCharge,ShipCharge,PayCharge";///Req/Search/Field
        String body = null;
        //拼接参数 并且 转成日本时间
        String xmlParam = this.getYahooOrderListParam(Result, Start, req.getOrderTimeFrom(), req.getOrderTimeTo(), req.getOrderId(), field, storeDo.getSellerId());
        if ("manual".equals(type)) {
            log.info("yahoo订单同步请求值：\r\n" + xmlParam);
        }
        body = YahooUtils.yahooDoPost("https://circus.shopping.yahooapis.jp/ShoppingWebService/V1/orderList", getYahooUserAccessToken(storeDo), storeDo.getSellerId(), storeDo.getAppSecret(), storeDo.getSignatureVersion(), xmlParam);
        return body;
    }


    public OrderYahooEntity getYahooOrderDetailInfo(PlatformStoreDo storeDo, String orderId, String type) {
        //参数格式是 xml格式
        String field = "OrderId,Version,ParentOrderId,BuyerComments,SellerComments,Notes,DeviceType,ShipZipCode,ShipPrefecture,ShipCity,ShipAddress1,ShipAddress2," +
                "ShipPhoneNumber,StoreStatus,SocialGiftType,LeadTimeText,LeadTimeStart,LeadTimeEnd," +
                "BillMailAddress,BillLastName,BillPrefecture,ShipStatus,ShipMethod,ShipMethodName,ShipRequestDate,ShipRequestTime,ShipNotes,ArrivalDate," +
                "SettlePayAmount,CombinedPayMethodAmount,UsePaypayPoint,"+
                "ShipCompanyCode,ReceiveShopCode,ShipInvoiceNumber1,ShipInvoiceNumber2,ShipInvoiceNumberEmptyReason,ShipUrl," +
                "LineId,ItemId,Title,SubCode,SubCodeOption,ItemOption,Inscription,UnitPrice,IsUsed,ProductId,Quantity,PointAvailQuantity";///Req/Search/Field


        String body = null;
        //拼接参数 并且 转成日本时间
        String xmlParam = "<Req>" +
                "<Target>" +
                "<OrderId>" + orderId + "</OrderId>" +
                "<Field>" + field + "</Field>" +
                "</Target>" +
                "<SellerId>" + storeDo.getSellerId() + "</SellerId>" +
                "</Req>";
        if ("manual".equals(type)) {
            log.info("yahoo订单详情请求值：\r\n" + xmlParam);
        }
        try {
            body = YahooUtils.yahooDoPost("https://circus.shopping.yahooapis.jp/ShoppingWebService/V1/orderInfo", getYahooUserAccessToken(storeDo), storeDo.getSellerId(), storeDo.getAppSecret(), storeDo.getSignatureVersion(), xmlParam);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if ("manual".equals(type)) {
            log.info("yahoo订单详情返回值：\r\n" + body);
        }
        if (StringUtils.isBlank(body)) {
            log.error("yahoo 获取订单详情 连接超时");
            return null;
        }

        //<?xml version='1.0' encoding='UTF-8' ?>
        //<Error>
        //<Message>AccessToken has been expired. This API session is shorter than another API.</Message>
        //<Code>px-04102</Code>
        //</Error>

        OrderYahooEntity orderYahooEntity = null;
        JSONObject object = null;
        try {
            object = XmlUtils.xml2Json(body);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (object == null) {
            log.error("Yahoo 获取订单列表 xml格式解析空指针");
            return null;
        } else if (StringUtils.isNotBlank(object.getString("Error"))) {
            String errorCode = object.getJSONObject("Error").getString("Code");
            if ("px-04102".equals(errorCode)) {
//                        platformStoreService.updateStoreAuthStatus(storeDo.getId());
            }
            log.error(object.getJSONObject("Error").getString("Message"));
            return null;
        } else {
            JSONObject jsonObject = object.getJSONObject("ResultSet").getJSONArray("Result").getJSONObject(0);
            orderYahooEntity = JSONArray.parseObject(jsonObject.getJSONArray("OrderInfo").getString(0), OrderYahooEntity.class);
        }
        return orderYahooEntity;
    }


    public String getYahooOrderListParam(int Result, int Start, Date OrderTimeFrom, Date OrderTimeTo, String orderId, String Field, String sellerId) {
        if (OrderTimeFrom == null || OrderTimeTo == null) {
            OrderTimeTo = TimeUtils.formatDateToDate(new Date(), "yyyyMMddHHmmss", "Asia/Tokyo");//乐天店铺转成日本时间
            OrderTimeFrom = DateUtils.passingDate(OrderTimeTo, -3);
        } else {
            OrderTimeTo = TimeUtils.formatDateToDate(OrderTimeTo, "yyyyMMddHHmmss", "Asia/Tokyo");
            OrderTimeFrom = TimeUtils.formatDateToDate(OrderTimeFrom, "yyyyMMddHHmmss", "Asia/Tokyo");
        }
        String from = DateUtils.dateFormatToString(OrderTimeFrom, "yyyyMMddHHmmss").replace(" ", "T");
        String to = DateUtils.dateFormatToString(OrderTimeTo, "yyyyMMddHHmmss").replace(" ", "T");

        StringBuilder builder = new StringBuilder("<Req><Search><Result>");
        builder.append(Result).append("</Result><Start>").append(Start).append("</Start>").append("<Sort>+order_time</Sort>");
        builder.append("<Condition>");
        if (org.apache.commons.lang3.StringUtils.isNotBlank(orderId)) {
            builder.append("<OrderId>").append(orderId).append("</OrderId>");
        } else {
            builder.append("<OrderTimeFrom>").append(from).append("</OrderTimeFrom><OrderTimeTo>").append(to).append("</OrderTimeTo>");
        }
        builder.append("</Condition>").append("<Field>").append(Field).append("</Field></Search><SellerId>").append(sellerId).append("</SellerId></Req>");
        return builder.toString();
    }


    @Override
    public String getYahooUserAccessToken(Long storeId) {
        PlatformStoreDo storeDo = platformStoreDao.selectById(storeId);
        if (storeDo == null) return null;
        return this.getYahooUserAccessToken(storeDo);
    }


    /**
     * Yahoo用户刷新令牌 换取 用户访问令牌
     *
     * @return
     */
    public String getYahooUserAccessToken(PlatformStoreDo storeDo) {
        Object accessToken = redisTemplate.opsForValue().get(RedisKeyConstants.YAHOOACCESSTOKEN_STORE + storeDo.getId());
        if (accessToken != null) {
            return accessToken.toString();
        }
        //accessToken过期， 用refresh_token 换取 accessToken过期
        Object refresh_token = redisTemplate.opsForValue().get(RedisKeyConstants.YAHOOREFRESHTOKEN_STORE + storeDo.getId());
        if (refresh_token == null) {
            refresh_token = storeDo.getServiceToken();
        }
        if (refresh_token == null || StringUtils.isBlank(refresh_token.toString())) {
            return null;
        }
        //refresh_token 含有特殊符号 需要转义一下 v^1.1#i^1#f^0#p^3#I^3#r^1#t^Ul4xMF83OkE2NUZFMTBENUZDOERBRkZEQUNFQjMxRTFBNjg3NzNFXzJfMSNFXjEyODQ=
//        refresh_token = StringUtils.replace(refresh_token.toString(), "#", "%23");
        String body = null;
        Map<String, String> paramMap = new HashMap<>();
//        grant_type=authorization_code&code=" + code + "&redirect_uri=" + redirectUri
        paramMap.put("grant_type", "refresh_token");
        paramMap.put("refresh_token", refresh_token.toString());
        try {
            body = YahooUtils.yahooDoPostAuth("https://auth.login.yahoo.co.jp/yconnect/v2/token", storeDo.getSellerId(), storeDo.getAppSecret(), storeDo.getSignatureVersion(), paramMap);
            //{"error":"invalid_grant","error_description":"the provided authorization refresh token is invalid or was issued to another client"}
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (StringUtils.isBlank(body)) {
            throw new BizException(SysConstant.Error_Connect_Timeout);
        }
        JSONObject jsonObject = JSONObject.parseObject(body);
        String access_token = jsonObject.getString("access_token");
        if (StringUtils.isBlank(access_token)) {
            String error = jsonObject.getString("error_description");
            if (error.contains("the provided authorization refresh token is invalid or was issued to another client")) {//todo 验证 返回值
                //刷新令牌过期，去更改店铺的授权状态 已过期 todo rabbitmq去做
                platformStoreService.updateStoreAuthStatus(storeDo.getId());
            }
            return "";
        }
        int expires_in = jsonObject.getInteger("expires_in");
        String token_type = jsonObject.getString("token_type");// User Access Token
        redisTemplate.opsForValue().set(RedisKeyConstants.YAHOOACCESSTOKEN_STORE + storeDo.getId(), access_token, expires_in - 30, TimeUnit.SECONDS);
        return access_token;
    }


    @Override
    public void consumerUpdate(RakutenOrderUpdateConReq req) {
        if (CollUtil.isEmpty(req.getEts())) {
            return;
        }
        for (PlatformOrderDo et : req.getEts()) {
            platformOrderDao.updateById(et);
        }
        List<String> collect = req.getEts().stream().filter(et -> et.getStatus() != null && et.getStatus() == 4).map(PlatformOrderDo::getPlatformOrderNo).collect(Collectors.toList());
        if (CollUtil.isEmpty(collect)) {
            return;
        }
        rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformOrder_CancelForBound, JSON.toJSONString(new PlatformOrderCancelReq(req.getEts().get(0).getStoreId(), collect)));
    }


    /**
     * 标发
     */
    @Override
    public void makeShip(YahooMarkShipConsumerReq req) {
        PlatformStoreDo storeDo = platformStoreDao.selectById(req.getStoreId());
        if (storeDo == null) {
            return;
        }
        Map<String, List<Long>> failMap = new HashMap<>();
        List<Long> successId = new ArrayList<>();
        List<YahooMarkStatusReq> markStatusReqList = new ArrayList<>();
        List<OperateRecordDo> operateRecordDos = new ArrayList<>();
        //拼接参数
        int count = 0;
        for (YahooMarkShipReq mark : req.getMarks()) {
            StringBuilder builder = new StringBuilder();
            builder.append("<Req>").append("<Target>");
            builder.append("<OrderId>").append(mark.getOrderNo()).append("</OrderId>");
            builder.append("<IsPointFix>").append(true).append("</IsPointFix>");
//            builder.append("<OperationUser>").append("筋斗云").append("</OperationUser>");
            builder.append("</Target>");
            builder.append("<Order><Ship>");
            builder.append("<ShipStatus>").append(3).append("</ShipStatus>");
//            builder.append("<ShipMethod>").append("").append("</ShipMethod>");
//            builder.append("<ShipNotes>").append(3).append("</ShipNotes>");
            builder.append("<ShipCompanyCode>").append(mark.getLogisticsCarrier()).append("</ShipCompanyCode>");
            if (mark.getTrackNo().contains(",")) {
                String[] split = mark.getTrackNo().split(",");
                builder.append("<ShipInvoiceNumber1>").append(split[0].replaceAll("-", "").trim()).append("</ShipInvoiceNumber1>");
                builder.append("<ShipInvoiceNumber2>").append(split[1].replaceAll("-", "").trim()).append("</ShipInvoiceNumber2>");
            } else {
                builder.append("<ShipInvoiceNumber1>").append(mark.getTrackNo().replaceAll("-", "").trim()).append("</ShipInvoiceNumber1>");
            }
//            builder.append("<ShipInvoiceNumber2>").append("").append("</ShipInvoiceNumber2>");
//            builder.append("<ShipUrl>").append("").append("</ShipUrl>");
            builder.append("<ShipDate>").append(mark.getShipTimeStr()).append("</ShipDate>");//todo 日本時間
//            builder.append("<ArrivalDate>").append("20140921").append("</ArrivalDate>");
            builder.append("</Ship></Order>");
            builder.append("<SellerId>").append(storeDo.getSellerId()).append("</SellerId>");
            builder.append("</Req>");
            if (count < 2) {
                log.info("yahoo标发开始，参数：" + builder);
            }
            String body = null;
            try {
                body = YahooUtils.yahooDoPost("https://circus.shopping.yahooapis.jp/ShoppingWebService/V1/orderShipStatusChange", getYahooUserAccessToken(storeDo), storeDo.getSellerId(), storeDo.getAppSecret(), storeDo.getSignatureVersion(), builder.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (count < 2) {
                log.info("yahoo标发結束，返回：" + body);
            }
            if (StringUtils.isBlank(body)) {
                //更新标发状态 失败
                failMap.computeIfAbsent("yahoo服务器连接超时", k -> new ArrayList<>()).add(mark.getId());
                continue;
            }
            JSONObject object = null;
            try {
                object = XmlUtils.xml2Json(body);
            } catch (JDOMException | IOException e) {
                e.printStackTrace();
            }
            if (object == null) {
                failMap.computeIfAbsent("xml格式解析空指针", k -> new ArrayList<>()).add(mark.getId());
            } else if (StringUtils.isNotBlank(object.getString("Error"))) {
                String errorCode = object.getJSONObject("Error").getString("Code");
                if ("px-04102".equals(errorCode)) {
//                        platformStoreService.updateStoreAuthStatus(storeDo.getId());
                }
                failMap.computeIfAbsent(object.getJSONObject("Error").getString("Message"), k -> new ArrayList<>()).add(mark.getId());
                OperateRecordDo operateRecordDo = new OperateRecordDo(mark.getId(), OperateTypeEnum.PlatformOrder.getValue(), SysConstant.platformOrder_MarkShip_Fail,
                        req.getUserId(), new Date(), errorCode + "_" + object.getJSONObject("Error").getString("Message"));
                operateRecordDos.add(operateRecordDo);
            } else {
                successId.add(mark.getId());
                markStatusReqList.add(new YahooMarkStatusReq(mark.getId(), mark.getOrderNo()));
                OperateRecordDo operateRecordDo = new OperateRecordDo(mark.getId(), OperateTypeEnum.PlatformOrder.getValue(), SysConstant.platformOrder_MarkShip, req.getUserId(), new Date(), mark.getTrackNo());
                operateRecordDos.add(operateRecordDo);
            }
            count++;
        }
        if (!successId.isEmpty()) {
            platformOrderService.updateMarkStatusForFinish(successId, 3, null, StaticDict.Store_PlatformCode.Yahoo.getValue());
        }
        for (String failReason : failMap.keySet()) {
            platformOrderService.updateMarkStatus(failMap.get(failReason), 2, failReason);
        }
        rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformStore_Yahoo_MarkStatus, JSON.toJSONString(new YahooMarkStatusConsumer(markStatusReqList, 5, storeDo, req.getUserId())));
        //操作记录
        if (!operateRecordDos.isEmpty()) {
            List<List<OperateRecordDo>> partition = ListUtil.partition(operateRecordDos, 3000);
            for (List<OperateRecordDo> partList : partition) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.CrossMall_CMOrder_OperateList, JSON.toJSONString(partList));
            }
        }
    }

    @Override
    public void makeSeen(YahooMarkStatusConsumer req) {
        List<YahooMarkStatusReq> list = req.getList();
        if (list == null || list.isEmpty()) {
            return;
        }
        String orderStatusStr = req.getOrderStatus() == 5 ? "已完成" : (req.getOrderStatus() == 4 ? "キャンセル" : (req.getOrderStatus() == 3 ? "保留" : (req.getOrderStatus() == 2 ? "処理中" : "予約中")));

        PlatformStoreDo storeDo = req.getStoreDo();
        Map<String, List<Long>> failMap = new HashMap<>();
        List<Long> successId = new ArrayList<>();
        List<OperateRecordDo> operateRecordDos = new ArrayList<>();
        for (YahooMarkStatusReq statusReq : list) {
            StringBuilder markFinish = new StringBuilder();
            markFinish.append("<Req>").append("<Target>");
            markFinish.append("<OrderId>").append(statusReq.getOrderId()).append("</OrderId>");
            markFinish.append("</Target>");
            markFinish.append("<Order>");
            markFinish.append("<IsSeen>").append(true).append("</IsSeen>");
            // markFinish.append("<OrderStatus>").append(req.getOrderStatus()).append("</OrderStatus>");
            markFinish.append("</Order>");
            markFinish.append("<SellerId>").append(storeDo.getSellerId()).append("</SellerId>");
            markFinish.append("</Req>");
           log.error("yahoo makeSeen 参数:" + markFinish);
            String body = null;
            try {
                body = YahooUtils.yahooDoPost("https://circus.shopping.yahooapis.jp/ShoppingWebService/V1/orderChange", getYahooUserAccessToken(storeDo), storeDo.getSellerId(), storeDo.getAppSecret(), storeDo.getSignatureVersion(), markFinish.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
            //<Error><Code>od90103</Code><Message>Update Check Error</Message><Detail></Detail></Error>
            //<?xml version="1.0" encoding="UTF-8" ?><ResultSet totalResultsAvailable="1" totalResultsReturned="1" ok="1" ng="0">
            // <Result><Status>OK</Status><Warning><Code>od91705</Code><Message></Message><Detail></Detail></Warning></Result></ResultSet>
           log.error("yahoo makeSeen 返回值:" + body);
            if (StringUtils.isBlank(body)) {
                //更新标发状态 失败
                log.error("yahoo服务器连接超时");
                failMap.computeIfAbsent("yahoo服务器连接超时", k -> new ArrayList<>()).add(statusReq.getId());
                continue;
            }
            JSONObject object = null;
            log.info("Yahoo返回数据orderSeenChange");
            log.info(body);
            try {
                object = XmlUtils.xml2Json(body);
            } catch (JDOMException | IOException e) {
                e.printStackTrace();
            }
            if (object == null) {
                failMap.computeIfAbsent("xml格式解析空指针", k -> new ArrayList<>()).add(statusReq.getId());
            } else if (StringUtils.isNotBlank(object.getString("Error"))) {
                String errorCode = object.getJSONObject("Error").getString("Code");
                if ("px-04102".equals(errorCode)) {
//                        platformStoreService.updateStoreAuthStatus(storeDo.getId());
                }
                failMap.computeIfAbsent(object.getJSONObject("Error").getString("Message"), k -> new ArrayList<>()).add(statusReq.getId());
                OperateRecordDo operateRecordDo = new OperateRecordDo(statusReq.getId(), OperateTypeEnum.PlatformOrder.getValue(), SysConstant.platformOrder_MarkFinish_Fail,
                        req.getUserId(), new Date(), orderStatusStr + SysConstant.operateRecordSeparator + errorCode + "_" + object.getJSONObject("Error").getString("Message"));
                operateRecordDos.add(operateRecordDo);
            } else {
                successId.add(statusReq.getId());
                OperateRecordDo operateRecordDo = new OperateRecordDo(statusReq.getId(), OperateTypeEnum.PlatformOrder.getValue(), SysConstant.platformOrder_MarkFinish, req.getUserId(), new Date(), orderStatusStr);
                operateRecordDos.add(operateRecordDo);
            }
        }
        if (!successId.isEmpty()) {
            Integer markFinishStatus = null;
            if (5 == req.getOrderStatus()) {
                //订单完成
                markFinishStatus = 3;
            } else if (2 == req.getOrderStatus()) {
                markFinishStatus = 30;
            }
            if (markFinishStatus != null) {
                LambdaUpdateWrapper<PlatformOrderDo> updateWrapper = new LambdaUpdateWrapper<PlatformOrderDo>();
                updateWrapper.in(PlatformOrderDo::getId, successId);
                updateWrapper.set(PlatformOrderDo::getMarkFinishStatus, markFinishStatus);
                updateWrapper.set(PlatformOrderDo::getMarkShipmentFailReason, null);
                this.update(updateWrapper);
            }
        }
        Integer failMarkStatus = null;
        if (5 == req.getOrderStatus()) {
            //订单完成
            failMarkStatus = 2;
        } else if (2 == req.getOrderStatus()) {
            failMarkStatus = 20;
        }
        if (failMarkStatus != null) {
            for (String failReason : failMap.keySet()) {
                LambdaUpdateWrapper<PlatformOrderDo> updateWrapper = new LambdaUpdateWrapper<PlatformOrderDo>();
                updateWrapper.in(PlatformOrderDo::getId, failMap.get(failReason));
                updateWrapper.set(PlatformOrderDo::getMarkShipmentFailReason, failReason);
                updateWrapper.set(PlatformOrderDo::getMarkFinishStatus, failMarkStatus);
                this.update(updateWrapper);
            }
        }
        //操作记录
        if (!operateRecordDos.isEmpty()) {
            List<List<OperateRecordDo>> partition = ListUtil.partition(operateRecordDos, 3000);
            for (List<OperateRecordDo> partList : partition) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.CrossMall_CMOrder_OperateList, JSON.toJSONString(partList));
            }
        }
        // rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformOrder_Auto_SendMail, JSON.toJSONString(new SendMailConsumerReq(req.getOrderStatus() == 2 ? 1 : 2, storeDo.getId(), successId)));

    }



    @Override
    public void makeFinish(YahooMarkStatusConsumer req) {
        List<YahooMarkStatusReq> list = req.getList();
        if (list == null || list.isEmpty()) {
            return;
        }
        String orderStatusStr = req.getOrderStatus() == 5 ? "已完成" : (req.getOrderStatus() == 4 ? "キャンセル" : (req.getOrderStatus() == 3 ? "保留" : (req.getOrderStatus() == 2 ? "処理中" : "予約中")));

        PlatformStoreDo storeDo = req.getStoreDo();
        Map<String, List<Long>> failMap = new HashMap<>();
        List<Long> successId = new ArrayList<>();
        List<OperateRecordDo> operateRecordDos = new ArrayList<>();
        for (YahooMarkStatusReq statusReq : list) {
            StringBuilder markFinish = new StringBuilder();
            markFinish.append("<Req>").append("<Target>");
            markFinish.append("<OrderId>").append(statusReq.getOrderId()).append("</OrderId>");
            markFinish.append("<IsPointFix>").append(true).append("</IsPointFix>");
            markFinish.append("</Target>");
            markFinish.append("<Order>");
            markFinish.append("<OrderStatus>").append(req.getOrderStatus()).append("</OrderStatus>");
            markFinish.append("</Order>");
            markFinish.append("<SellerId>").append(storeDo.getSellerId()).append("</SellerId>");
            markFinish.append("</Req>");
//            log.error("yahoo makeFinish 参数:" + markFinish);
            String body = null;
            try {
                body = YahooUtils.yahooDoPost("https://circus.shopping.yahooapis.jp/ShoppingWebService/V1/orderStatusChange", getYahooUserAccessToken(storeDo), storeDo.getSellerId(), storeDo.getAppSecret(), storeDo.getSignatureVersion(), markFinish.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
            //<Error><Code>od90103</Code><Message>Update Check Error</Message><Detail></Detail></Error>
            //<?xml version="1.0" encoding="UTF-8" ?><ResultSet totalResultsAvailable="1" totalResultsReturned="1" ok="1" ng="0">
            // <Result><Status>OK</Status><Warning><Code>od91705</Code><Message></Message><Detail></Detail></Warning></Result></ResultSet>
//            log.error("yahoo makeFinish 返回值:" + body);
            if (StringUtils.isBlank(body)) {
                //更新标发状态 失败
                log.error("yahoo服务器连接超时");
                failMap.computeIfAbsent("yahoo服务器连接超时", k -> new ArrayList<>()).add(statusReq.getId());
                continue;
            }
            JSONObject object = null;
            log.info("Yahoo返回数据orderStatusChange");
            log.info(body);
            try {
                object = XmlUtils.xml2Json(body);
            } catch (JDOMException | IOException e) {
                e.printStackTrace();
            }
            if (object == null) {
                failMap.computeIfAbsent("xml格式解析空指针", k -> new ArrayList<>()).add(statusReq.getId());
            } else if (StringUtils.isNotBlank(object.getString("Error"))) {
                String errorCode = object.getJSONObject("Error").getString("Code");
                if ("px-04102".equals(errorCode)) {
//                        platformStoreService.updateStoreAuthStatus(storeDo.getId());
                }
                failMap.computeIfAbsent(object.getJSONObject("Error").getString("Message"), k -> new ArrayList<>()).add(statusReq.getId());
                OperateRecordDo operateRecordDo = new OperateRecordDo(statusReq.getId(), OperateTypeEnum.PlatformOrder.getValue(), SysConstant.platformOrder_MarkFinish_Fail,
                        req.getUserId(), new Date(), orderStatusStr + SysConstant.operateRecordSeparator + errorCode + "_" + object.getJSONObject("Error").getString("Message"));
                operateRecordDos.add(operateRecordDo);
            } else {
                successId.add(statusReq.getId());
                OperateRecordDo operateRecordDo = new OperateRecordDo(statusReq.getId(), OperateTypeEnum.PlatformOrder.getValue(), SysConstant.platformOrder_MarkFinish, req.getUserId(), new Date(), orderStatusStr);
                operateRecordDos.add(operateRecordDo);
            }
        }
        if (!successId.isEmpty()) {
            Integer markFinishStatus = null;
            if (5 == req.getOrderStatus()) {
                //订单完成
                markFinishStatus = 3;
            } else if (2 == req.getOrderStatus()) {
                markFinishStatus = 30;
            }
            if (markFinishStatus != null) {
                LambdaUpdateWrapper<PlatformOrderDo> updateWrapper = new LambdaUpdateWrapper<PlatformOrderDo>();
                updateWrapper.in(PlatformOrderDo::getId, successId);
                updateWrapper.set(PlatformOrderDo::getMarkFinishStatus, markFinishStatus);
                updateWrapper.set(PlatformOrderDo::getMarkShipmentFailReason, null);
                this.update(updateWrapper);
            }
        }
        Integer failMarkStatus = null;
        if (5 == req.getOrderStatus()) {
            //订单完成
            failMarkStatus = 2;
        } else if (2 == req.getOrderStatus()) {
            failMarkStatus = 20;
        }
        if (failMarkStatus != null) {
            for (String failReason : failMap.keySet()) {
                LambdaUpdateWrapper<PlatformOrderDo> updateWrapper = new LambdaUpdateWrapper<PlatformOrderDo>();
                updateWrapper.in(PlatformOrderDo::getId, failMap.get(failReason));
                updateWrapper.set(PlatformOrderDo::getMarkShipmentFailReason, failReason);
                updateWrapper.set(PlatformOrderDo::getMarkFinishStatus, failMarkStatus);
                this.update(updateWrapper);
            }
        }
        //操作记录
        if (!operateRecordDos.isEmpty()) {
            List<List<OperateRecordDo>> partition = ListUtil.partition(operateRecordDos, 3000);
            for (List<OperateRecordDo> partList : partition) {
                rabbitTemplate.convertAndSend(RabbitQueueConstant.CrossMall_CMOrder_OperateList, JSON.toJSONString(partList));
            }
        }
        rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformOrder_Auto_SendMail, JSON.toJSONString(new SendMailConsumerReq(req.getOrderStatus() == 2 ? 1 : 2, storeDo.getId(), successId)));

    }


    @Override
    public void makeFinish() {
        List<PlatformOrderDo> platformOrderDoList = platformOrderDao.selectList(new LambdaQueryWrapper<PlatformOrderDo>()
                .select(PlatformOrderDo::getId, PlatformOrderDo::getPlatformOrderNo, PlatformOrderDo::getMarkFinishStatus, PlatformOrderDo::getStoreId)
                .eq(PlatformOrderDo::getStatus, 3)
                .eq(PlatformOrderDo::getMarkFinishStatus, 5).eq(PlatformOrderDo::getPlatformCode, "yahoo")
                .gt(PlatformOrderDo::getMarkShipmentTime, DateUtils.dateFormatToString(DateUtils.passingDate(new Date(), -3))));
        Map<Long, List<PlatformOrderDo>> collect = platformOrderDoList.stream().collect(Collectors.groupingBy(PlatformOrderDo::getStoreId));
        for (Long l : collect.keySet()) {
            PlatformStoreDo storeDo = platformStoreService.getById(l);
            List<YahooMarkStatusReq> markStatusReqList = new ArrayList<>();
            for (PlatformOrderDo orderDo : collect.get(l)) {
                markStatusReqList.add(new YahooMarkStatusReq(orderDo.getId(), orderDo.getPlatformOrderNo()));
            }
            rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformStore_Yahoo_MarkStatus, JSON.toJSONString(new YahooMarkStatusConsumer(markStatusReqList, 5, storeDo, null)));
        }
    }


    @Override
    public String testSetStock() {
        List<YahooSkuSetStockReq> skuSetStockReqs = new ArrayList<>();
        skuSetStockReqs.add(new YahooSkuSetStockReq("e2-7", "e2-7-44l", "20"));

        PlatformStoreDo storeDoList = platformStoreDao.selectOne(new LambdaQueryWrapper<PlatformStoreDo>().eq(PlatformStoreDo::getPlatformCode, "yahoo")
                .eq(PlatformStoreDo::getAuthSyncStatus, 1).isNotNull(PlatformStoreDo::getAppSecret).isNotNull(PlatformStoreDo::getSellerId).last(" limit 1"));
        if (storeDoList == null) {
            return null;
        }
        return this.setStock(skuSetStockReqs, storeDoList, null);
    }

    @Override
    public void setStockByStoreId(StockToPlatformConsumerReq req) {
        PlatformStoreDo storeDo = platformStoreDao.selectById(req.getStoreId());
        if (storeDo == null) {
            return;
        }
        AsyncJobDo asyncJobDo = asyncJobDao.selectById(req.getJobId());
        if (asyncJobDo == null) {
            return;
        }
        //告诉他正在处理
        rabbitTemplate.convertAndSend(RabbitQueueConstant.Export_AsyncDo_Update, JSON.toJSONString(new AsyncUpdateConsumerReq(req.getJobId(), 1, 0, null)));
        List<PlatformSkuStockDto> skuStockList = productStockDao.getPlatformSkuStockList(Arrays.asList(StaticDict.Store_PlatformCode.Yahoo.getValue()),
                req.getPlatformSkuList(), null, req.getInventoryLink(), Arrays.asList(req.getStoreId()));
        if (skuStockList.isEmpty()) {
            rabbitTemplate.convertAndSend(RabbitQueueConstant.Export_AsyncDo_Finish, JSON.toJSONString(new AsyncFinishConsumerReq(req.getJobId(), null)));
            return;
        }
        String stockRedisKey = RedisKeyConstants.PLATFORMSTOCK_KEY_PREFIX +customer +"_"+ req.getStoreId();
        // 新增：存储SKU ID与对应库存的映射（用于ZSet的score）
        Map<Long, Integer> small20IdToQuantityMap = new HashMap<>();
        List<YahooSkuSetStockReq> skuSetStockList = new ArrayList<>();
        for (PlatformSkuStockDto skuStock : skuStockList) {
            if (StringUtils.isBlank(skuStock.getParentSku())) {
                continue;
            }
            if (skuStock.getSystemProductId() == null) {
                continue;
            }
            double stockPercent = skuStock.getStockPercent() < 0 || skuStock.getStockPercent() > 1 ? 0.95 : 1 - skuStock.getStockPercent();
            int quantity = (int) (skuStock.getLeftQty() / skuStock.getQty() * stockPercent);
            quantity = Math.min(Math.max(quantity, 0), 999998);
            if (quantity > 0 && quantity <= 20) {
                small20IdToQuantityMap.put(skuStock.getId(), quantity); // 保存ID与库存的映射
            }
            if (quantity <= 20 && req.getUserId() == null) {
                quantity = 0;
            }
            Integer lastQuantity = platformOrderService.getLastRecordedInventory(stockRedisKey, skuStock.getPlatformSku());
            if (lastQuantity == null || !lastQuantity.equals(quantity) || req.getUserId() != null) {
                skuSetStockList.add(new YahooSkuSetStockReq(skuStock.getId(), skuStock.getParentSku(), skuStock.getPlatformSku(), quantity + ""));
            }
        }
        List<List<YahooSkuSetStockReq>> partition = ListUtil.partition(skuSetStockList, 1000);

        try {
            String key = asyncJobDo.getJobType() + "_count_"  + "_" + asyncJobDo.getCompanyId() + "_" + customer + "_" + DateUtils.dateFormatToString(new Date(), "yyyyMMdd");
            long count = redisTemplate.opsForValue().increment(key, 1);
            redisTemplate.expire(key, 60 * 60 * 25, TimeUnit.SECONDS);
            String fileName = asyncJobDo.getJobName() + "_" + count + ".csv";
            String tempFilePath = omsFilePath + fileName;
            CsvExportParams csvParam = new CsvExportParams();
//            csvParam.setEncoding("Shift-JIS");
            csvParam.setEncoding("UTF-8");
            csvParam.setTextMark("");
            CsvUtils.initCsv(tempFilePath, csvParam, StockToPlatformRecordVo.class);
            //记录一下 店铺 平台sku 上一次回传数量
            Map<String, String> thisStockMap = new HashMap<>();
            for (List<YahooSkuSetStockReq> yahooSkuSetStockReqs : partition) {
                String string = this.setStock(yahooSkuSetStockReqs, storeDo, req.getUserId());
                List<StockToPlatformRecordVo> list = new ArrayList<>();
                for (YahooSkuSetStockReq stockReq : yahooSkuSetStockReqs) {
                    if (StringUtils.isBlank(string)) {
                        thisStockMap.put(stockReq.getItemCode(), String.valueOf(stockReq.getQuantity()));
                        list.add(new StockToPlatformRecordVo(stockReq.getParentSku(), stockReq.getItemCode(), Integer.parseInt(stockReq.getQuantity()), "成功", null));
                    } else {
                        list.add(new StockToPlatformRecordVo(stockReq.getParentSku(), stockReq.getItemCode(), Integer.parseInt(stockReq.getQuantity()), "失败", string));
                    }
                }
                CsvUtils.appendToCsv(tempFilePath, list);
                rabbitTemplate.convertAndSend(RabbitQueueConstant.Export_AsyncDo_Update, JSON.toJSONString(new AsyncUpdateConsumerReq(req.getJobId(), null, list.size(), null)));
            }
            if (!thisStockMap.isEmpty() && req.getUserId() == null) {
                //记录本次回传数量至redis
                redisTemplate.opsForHash().putAll(stockRedisKey, thisStockMap);
//                redisTemplate.expire(stockRedisKey, 8, TimeUnit.HOURS);
            }
            //本机文件传到oms上
            String objectNameTemporary = "";
            if (filePath.startsWith("http")) {
                try {
                    objectNameTemporary = AliOss.temporaryFile2(fileName, new File(tempFilePath));
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("导出文件失败" + e.getMessage());
                }
            }
            //文件路径
            String path = fileUrl + objectNameTemporary;
            rabbitTemplate.convertAndSend(RabbitQueueConstant.Export_AsyncDo_Finish, JSON.toJSONString(new AsyncFinishConsumerReq(req.getJobId(), path)));
        } catch (Exception e) {
            e.printStackTrace();
            rabbitTemplate.convertAndSend(RabbitQueueConstant.Export_AsyncDo_Update, JSON.toJSONString(new AsyncUpdateConsumerReq(req.getJobId(), 4, 0, e.getMessage())));
        }
        //用ZSet处理低库存SKU的存储与对比
        rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformProduct_InventoryLess, JSON.toJSONString(new UpdateInventoryLessReq(req.getStoreId(), req.getUserId(), small20IdToQuantityMap)));
    }



        @Override
    public String setStock(List<YahooSkuSetStockReq> skuSetStockReqs, PlatformStoreDo storeDo, Long userId) {
        if (skuSetStockReqs.isEmpty()) {
            return null;
        }
        //*如果库存数据库中没有记录，则会创建一条新记录。
        //*如果指定的产品代码出现一个错误，所有更新都将被取消。
        //每次最多1000 个
        String body = null;
        //拼接参数 并且 转成日本时间
        StringBuilder builder1 = new StringBuilder();
        try {
            builder1.append("seller_id=").append(URLEncoder.encode(storeDo.getSellerId(), "UTF-8"));
            builder1.append("&item_code=").append(URLEncoder.encode(StringUtils.join(skuSetStockReqs.stream().map(i -> i.getParentSku() + ":" + i.getItemCode()).collect(Collectors.toList()), ","), "UTF-8"));
            builder1.append("&quantity=").append(URLEncoder.encode(StringUtils.join(skuSetStockReqs.stream().map(YahooSkuSetStockReq::getQuantity).collect(Collectors.toList()), ","), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return "参数拼接失败";
        }
//        log.error("yahoo 更新库存 请求值:\r\n"+builder1.toString());
        body = YahooUtils.yahooDoPost("https://circus.shopping.yahooapis.jp/ShoppingWebService/V1/setStock", getYahooUserAccessToken(storeDo), storeDo.getSellerId(), storeDo.getAppSecret(), storeDo.getSignatureVersion(), builder1.toString());
        JSONObject object = null;
        List<OrderYahooEntity> list = null;
        try {
            object = XmlUtils.xml2Json(body);
        } catch (JDOMException | IOException e) {
            e.printStackTrace();
        }
        if (skuSetStockReqs.size() == 1) {
            log.error("yahoo 回传库存返回值：" + body);
        }
        if (object == null) {
            return "Yahoo 更新库存 xml格式解析空指针";
        } else if (StringUtils.isNotBlank(object.getString("Error"))) {
            String errorCode = object.getJSONObject("Error").getString("Code");
            if ("px-04102".equals(errorCode)) {
                platformStoreService.updateStoreAuthStatus(storeDo.getId());
            }
            return object.getJSONObject("Error").getString("Message");
        } else {
            JSONArray array = object.getJSONObject("ResultSet").getJSONArray("Result");
//            log.error("yahoo 更新库存 数组："+array.toJSONString());
        }
//        log.error("yahoo 更新库存 json返回值:\r\n"+object.toJSONString());
//        log.error("yahoo 更新库存返回值\r\n" + body);
        //<?xml version="1.0" encoding="UTF-8"?>
        //<ResultSet totalResultsAvailable="1" totalResultsReturned="1" firstResultPosition="1" ok="1" ng="0">
        //  <Result>
        //    <ItemCode>1</ItemCode>
        //    <SubCode></SubCode>
        //    <Quantity></Quantity>
        //  </Result>
        //</ResultSet>

        //<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Error><Message>seller_id is not specified</Message><Code>px-04001</Code></Error>
        //操作日志 todo
//        List<OperateRecordDo> operateRecordDos = new ArrayList<>();
//        for (YahooSkuSetStockReq inventory : skuSetStockReqs) {
//            String message = StaticDict.Store_PlatformCode.Yahoo.getValue()  + SysConstant.operateRecordSeparator + storeDo.getStoreName()+ SysConstant.operateRecordSeparator+ inventory.getParentSku() + SysConstant.operateRecordSeparator + inventory.getItemCode() + SysConstant.operateRecordSeparator + inventory.getQuantity();
//            OperateRecordDo operateRecordDo = new OperateRecordDo(inventory.getId(), OperateTypeEnum.PlatProduct_StockTO_Platform.getValue(),
//                    SysConstant.platProduct_StockTo_Platform_SUCCESS,
//                    userId, new Date(), message);
//            operateRecordDos.add(operateRecordDo);
//        }
//        if (!operateRecordDos.isEmpty()) {
//            List<List<OperateRecordDo>> recordPartition = ListUtil.partition(operateRecordDos, 3000);
//            for (List<OperateRecordDo> partList : recordPartition) {
//                rabbitTemplate.convertAndSend(RabbitQueueConstant.CrossMall_CMOrder_OperateList, JSON.toJSONString(partList));
//            }
//        }
        return null;
    }

    @Override
    public void getItemList() {
        List<PlatformStoreDo> storeDoList = platformStoreDao.selectList(new LambdaQueryWrapper<PlatformStoreDo>().eq(PlatformStoreDo::getPlatformCode, "yahoo")
                .eq(PlatformStoreDo::getAuthSyncStatus, 1).isNotNull(PlatformStoreDo::getAppSecret).isNotNull(PlatformStoreDo::getSellerId));
        for (PlatformStoreDo platformStoreDo : storeDoList) {
            rabbitTemplate.convertAndSend(RabbitQueueConstant.GetPlatformProduct_ItemList_Yahoo, JSON.toJSONString(platformStoreDo));
        }
    }


    @Override
    public String getItemList(PlatformStoreDo storeDo) {
        int results = 49;   ///Req/Search/Result
        int start = 1;       ///Req/Search/Start
        //还有一个/Req/Search/Condition/

        Map<String, Object> map = new HashMap<>();
        map.put("results", results);
        map.put("seller_id", storeDo.getSellerId());
        map.put("appid", YahooUtils.clientId);
        int total = 0;
        Set<String> manageSkus = new HashSet<>();
        do {
            map.put("start", start);
            //拼接参数 并且 转成日本时间
            log.info("yahoo itemSearch " + "店铺：" + storeDo.getStoreCode() + ", 同步item请求: \r\n" + JSON.toJSONString(map));
            String body = null;
            try {
                body = YahooUtils.yahooDoGetV3("https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch", map);
            } catch (Exception e) {
                e.printStackTrace();
            }
            log.info("yahoo itemSearch " + "店铺：" + storeDo.getStoreCode() + ", 同步item返回: \r\n" + body);
            start += results;
            if (StringUtils.isBlank(body)) {
                return "连接超时";
            }
            JSONObject object = JSONObject.parseObject(body);
            if (object == null) {
                return "Yahoo itemSearch 格式解析空指针";
            } else if (StringUtils.isNotBlank(object.getString("Error"))) {
                return object.getJSONObject("Error").getString("Message");
            } else if (StringUtils.isNotBlank(object.getString("status"))) {
                return object.getString("Message");
            } else {
                ItemSearchResponse response = JSONObject.parseObject(body, ItemSearchResponse.class);
                if (response != null) {
                    total = response.getTotalResultsReturned();
                    if (response.getHits() != null) {
                        List<String> skus = response.getHits().stream().map(i -> i.getCode().replaceAll(storeDo.getSellerId() + "_", "")).collect(Collectors.toList());
                        manageSkus.addAll(skus);
                    }
                }
            }
        } while (total >= results && (start + results) < 1000);
        //由于yahoo 隐藏的sku获取不到 所以把删除功能关闭
//        if (!manageSkus.isEmpty()) {
//            LambdaQueryWrapper<PlatformProductDo> selectWrapper = new LambdaQueryWrapper<>();
//            selectWrapper.select(PlatformProductDo::getId);
//            selectWrapper.notIn(PlatformProductDo::getParentSku, manageSkus);
//            selectWrapper.eq(PlatformProductDo::getCompanyId, storeDo.getCompanyId());
//            selectWrapper.eq(PlatformProductDo::getStoreId, storeDo.getId());
//            List<PlatformProductDo> deleteDoList = platformProductService.getBaseMapper().selectList(selectWrapper);
//            List<Long> ids = deleteDoList.stream().map(PlatformProductDo::getId).collect(Collectors.toList());
//            if (!ids.isEmpty()) {
//                LambdaUpdateWrapper<PlatformProductDo> updateWrapper = new LambdaUpdateWrapper<>();
//                updateWrapper.in(PlatformProductDo::getId, ids);
//                updateWrapper.set(PlatformProductDo::getDeleteFlag, 1);
//                updateWrapper.set(PlatformProductDo::getDeleteBy, -2);
//                updateWrapper.set(PlatformProductDo::getDeleteTime, new Date());
//                platformProductService.update(updateWrapper);
//            }
//        }
        for (String skus : manageSkus) {
            rabbitTemplate.convertAndSend(RabbitQueueConstant.GetPlatformProduct_Yahoo, JSON.toJSONString(new YahooGetItemReq(skus, storeDo)));
        }
        return null;
    }


    @Override
    public String getItem(String itemCode, PlatformStoreDo storeDo) {
        Map<String, Object> map = new HashMap<>();
        map.put("seller_id", storeDo.getSellerId());
        map.put("item_code", itemCode);

        //拼接参数 并且 转成日本时间
        String body = null;
        try {
            body = YahooUtils.yahooDoGet("https://circus.shopping.yahooapis.jp/ShoppingWebService/V1/getItem", getYahooUserAccessToken(storeDo), map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (StringUtils.isBlank(body)) {
            return "连接超时";
        }
        List<PlatformProductDo> productDoList = new ArrayList<>();
        org.json.JSONObject object = XML.toJSONObject(body);
        if (object.has("Error")) {
            if (object.getJSONObject("Error").has("Message")) {
                return object.getJSONObject("Error").getString("Message");
            } else {
                return "Yahoo获取商品itemCode详情失败";
            }
        } else {
            String itemCodeJson = object.getJSONObject("ResultSet").getJSONObject("Result").getString("ItemCode");
            org.json.JSONObject result = object.getJSONObject("ResultSet").getJSONObject("Result");
            if (!result.has("SubCodes")) {
                return null;
            }
            org.json.JSONObject subCodes = result.optJSONObject("SubCodes");
            if (subCodes == null) {
                return null;
            }
            org.json.JSONArray subCodeList = subCodes.optJSONArray("SubCode");
            if (subCodeList == null || subCodeList.isEmpty()) {
                return null;
            }
            for (int i = 0; i < subCodeList.length(); i++) {
                org.json.JSONObject subCode = subCodeList.getJSONObject(i);
                PlatformProductDo productDo = new PlatformProductDo();
                productDo.setStoreId(storeDo.getId());
                productDo.setCompanyId(storeDo.getCompanyId());
                productDo.setTenantId(storeDo.getTenantId());
                productDo.setParentSku(itemCodeJson);
                productDo.setPlatformSku(subCode.getString("code"));
//                productDo.setPlatLeftQuantity(subCode.getInt("quantity"));
                if (subCode.has("stockClose")) {
                    productDo.setHideItem(subCode.getInt("stockClose"));
                }
                org.json.JSONArray option = subCode.optJSONArray("Option");
                if (option != null && !option.isEmpty()) {
                    org.json.JSONObject optionJson = option.getJSONObject(0);
                    String itemName = optionJson.getString("name");
                    itemName += "\r\n";
                    itemName += optionJson.getString("value");
                    productDo.setItemName(itemName);
                }
                org.json.JSONObject subCodeImage = subCode.getJSONObject("SubCodeImage");
                if (subCodeImage.has("exist_flag")) {
                    int existFlag = subCodeImage.optInt("exist_flag", 0);
                    if (existFlag == 1) {
                        String value = subCodeImage.getString("value");// grle.png
//                        String imageType = subCodeImage.getString("image_type");//lib
//                        String mainFlag = subCodeImage.getInt("main_flag");
                        String originalPriceEvidence = object.getJSONObject("ResultSet").getJSONObject("Result").getString("OriginalPriceEvidence");
                        if (originalPriceEvidence.lastIndexOf("/") != -1) {
                            originalPriceEvidence = originalPriceEvidence.substring(0, originalPriceEvidence.lastIndexOf("/") + 1);
                        }
                        originalPriceEvidence += value;
                        productDo.setImageUrl(originalPriceEvidence);
                    }
                }
                productDoList.add(productDo);
            }
        }
        //按照父sku 去分流更新
        Map<String, List<PlatformProductDo>> parentSkuMap = productDoList.stream().filter(i -> org.apache.commons.lang3.StringUtils.isNotBlank(i.getParentSku())).collect(Collectors.groupingBy(PlatformProductDo::getParentSku));
        for (String parentSkuKey : parentSkuMap.keySet()) {
            rabbitTemplate.convertAndSend(RabbitQueueConstant.PlatformProduct_Rakuten_UpdateByParentSKu, JSON.toJSONString(new PlatformProductSyncReq(parentSkuMap.get(parentSkuKey),
                    StaticDict.Store_PlatformCode.Rakuten.getValue(), true, storeDo.getId(), storeDo.getCompanyId())));
        }
//        platformProductService.save(productDoList, StaticDict.Store_PlatformCode.Yahoo.getValue(), storeDo.getId(), storeDo.getCompanyId());
        return null;
    }


    @Override
    public String updateYahooOrdersHistory(YahooConsumerReq req) {
        PlatformStoreDo storeDo = platformStoreDao.selectById(req.getStoreId());
        if (storeDo == null) {
            return SysConstant.Shop_NotExist;
        }

        Double time = redisTemplate.opsForZSet().score(RedisKeyConstants.orderSyncTimeSet_Yahoo_History + customer, storeDo.getId());
        if (time == null) {
            req.setOrderTimeFrom(DateUtils.passingDateYear(new Date(), -1));
        } else {
            req.setOrderTimeFrom(new Date(time.longValue()));
        }
        req.setOrderTimeTo(DateUtils.passingDateHour(req.getOrderTimeFrom(), 3));
        long day = DateUtil.betweenDay(req.getOrderTimeTo(), new Date(), true);
        if (day < 14) {
            return ("时间间隔不能小于14天");
        }
        int Result = 2000;   ///Req/Search/Result
        int Start = 1;       ///Req/Search/Start
        String Sort = "+order_time";///Req/Search/Sort
        //还有一个/Req/Search/Condition/

        //* 1需要 OrderID 或 OrderTime YYYYMMDDHH24MISS。
        //* 2如果未指定 OrderID，则需要 OrderTime、OrderTimeFrom、OrderTimeTo、PublicationTimeFrom 和 PublicationTimeTo 之一。
        //* 3指定以逗号分隔的多个匹配项。

        int total = 0;
        List<OrderYahooEntity> entities = new ArrayList<>();
        do {
            //拼接参数 并且 转成日本时间
            String body = this.getOrder(Result, Start, req, storeDo, "auto");
            Start += Result;
            if (StringUtils.isBlank(body)) {
                redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo_History + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                return "连接超时";
            }
            JSONObject object = null;
            List<OrderYahooEntity> list = null;
            try {
                object = XmlUtils.xml2Json(body);
            } catch (JDOMException | IOException e) {
                e.printStackTrace();
            }
            if (object == null) {
                redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo_History + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                return "Yahoo 获取订单列表 xml格式解析空指针";
            } else if (StringUtils.isNotBlank(object.getString("Error"))) {
                redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo_History + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                return object.getJSONObject("Error").getString("Message");
            } else {
                total = object.getJSONObject("Result").getJSONArray("Search").getJSONObject(0).getInteger("TotalCount");
                if (total == 0) {
                    list = new ArrayList<>();
                } else {
                    list = JSONArray.parseArray(object.getJSONObject("Result").getJSONArray("Search").getJSONObject(0).getString("OrderInfo"), OrderYahooEntity.class);
                }
            }
            if (list == null) {
                redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo_History + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                return "Yahoo 获取订单列表 xml格式解析 数组null";
            }
            entities.addAll(list);
        } while (total >= Result);

        //要插入的
        List<PlatformOrderCopyDo> platInertList = new ArrayList<>();

        //然后一条条 获取详情方法
        for (OrderYahooEntity entity : entities) {
            OrderYahooEntity detailEntity = this.getYahooOrderDetailInfo(storeDo, entity.getOrderId(), "auto");
            if (detailEntity == null) {
                redisTemplate.opsForZSet().add(RedisKeyConstants.orderSyncTimeSet_Yahoo_History + customer, storeDo.getId(), req.getOrderTimeFrom().getTime());
                break;
            }
            PlatformOrderCopyDo copyDo = new PlatformOrderCopyDo();
            copyDo.setCompanyId(storeDo.getCompanyId());
            copyDo.setStoreId(storeDo.getId());
            copyDo.setPlatformOrderNo(entity.getOrderId());
            copyDo.setOrderTime(DateUtils.stringFormatToDate(entity.getOrderTime(), "yyyy-MM-dd'T'hh:mm:ss"));
            copyDo.setPlatformCode(storeDo.getPlatformCode());
            copyDo.setOrderMsg(JSON.toJSONString(detailEntity));
            platInertList.add(copyDo);
        }
        platformOrderCopyService.batchInsert(platInertList);
        return "";
    }



}
